﻿using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace ImageProcess
{
    /// <summary>
    /// 查找图像助手类
    /// </summary>
    public class FindImageHelper
    {
        /// <summary>
        /// 图片包含匹配、大小图匹配带定位
        /// </summary>
        /// <param name="bigImagePath">大图路径</param>
        /// <param name="smallImagePath">小图路径</param>
        /// <param name="imgWidth">源图宽度（在源图上截取指定图标时源图的宽度，后期可能要对比的源图大小会不一样，例如同一个画面在不同手机下的截图大小应该不一样，这时要指定一个尺寸作为对比）</param>
        /// <param name="threshold">设定阈值，找到匹配位置</param>
        /// <returns>是否匹配</returns>
        public static bool IsImageContained(string bigImagePath,string smallImagePath, double imgWidth = 0, double threshold = 0.65f)
        {
            try
            {

                using Mat source = new Mat(bigImagePath);
                if (imgWidth != 0)
                {
                    //如果源图宽度不等于对比图的宽度则按照指定宽度等比例缩放（提高识别率）
                    if (source.Width != imgWidth)
                    {
                        Cv2.Resize(source, source, new OpenCvSharp.Size(imgWidth, imgWidth * source.Height / (double)source.Width));
                    }
                }

                using Mat pic = new Mat(smallImagePath, ImreadModes.AnyColor);
                using Mat result = new Mat();
                //模板匹配
                Cv2.MatchTemplate(source, pic, result, TemplateMatchModes.CCoeffNormed);
                Point minLoc, maxLoc;
                double minValue, maxValue;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);
                //Console.WriteLine(maxValue);
                if (maxValue >= threshold)
                {
                    //using Mat mask = source.Clone();
                    ////画框显示
                    //Cv2.Rectangle(mask, maxLoc, new Point(maxLoc.X + pic.Cols, maxLoc.Y + pic.Rows), Scalar.Yellow, 2);
                    //Cv2.ImShow("mask", mask);
                    //Cv2.WaitKey(0);
                    //Cv2.DestroyAllWindows();
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        public static double IsImageContainedScore(string bigImagePath, string smallImagePath, double imgWidth = 0, double threshold = 0.65f)
        {
            try
            {

                using Mat source = new Mat(bigImagePath);
                if (imgWidth != 0)
                {
                    //如果源图宽度不等于对比图的宽度则按照指定宽度等比例缩放（提高识别率）
                    if (source.Width != imgWidth)
                    {
                        Cv2.Resize(source, source, new OpenCvSharp.Size(imgWidth, imgWidth * source.Height / (double)source.Width));
                    }
                }

                using Mat pic = new Mat(smallImagePath, ImreadModes.AnyColor);
                using Mat result = new Mat();
                //模板匹配
                Cv2.MatchTemplate(source, pic, result, TemplateMatchModes.CCoeffNormed);
                Point minLoc, maxLoc;
                double minValue, maxValue;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);
                //Console.WriteLine(maxValue);

                return maxValue;
            }
            catch (Exception)
            {
                return 0;
            }
        }

        /// <summary>
        /// 图片包含匹配、大小图匹配带定位
        /// </summary>
        /// <param name="bigImagePath"></param>
        /// <param name="smallImagePath"></param>
        /// <param name="retc">输出的匹配结果</param>
        /// <param name="imgWidth"></param>
        /// <param name="threshold"></param>
        /// <returns>是否匹配</returns>
        public static bool IsImageContained(string bigImagePath, string smallImagePath, ref Model.RectangleMatch? retc, double imgWidth = 0, double threshold = 0.65f)
        {
            try
            {

                using Mat source = new Mat(bigImagePath);
                if (imgWidth != 0)
                {
                    //如果源图宽度不等于对比图的宽度则按照指定宽度等比例缩放（提高识别率）
                    if (source.Width != imgWidth)
                    {
                        Cv2.Resize(source, source, new OpenCvSharp.Size(imgWidth, imgWidth * source.Height / (double)source.Width));
                    }
                }

                using Mat pic = new Mat(smallImagePath, ImreadModes.AnyColor);
                using Mat result = new Mat();
                //模板匹配
                Cv2.MatchTemplate(source, pic, result, TemplateMatchModes.CCoeffNormed);
                Point minLoc, maxLoc;
                double minValue, maxValue;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);
                //Console.WriteLine(maxValue);
                if (maxValue >= threshold)
                {
                    using Mat mask = source.Clone();
                    //画框显示
                    Cv2.Rectangle(mask, maxLoc, new Point(maxLoc.X + pic.Cols, maxLoc.Y + pic.Rows), Scalar.Yellow, 2);
                    if (retc == null)
                    {
                        retc = new Model.RectangleMatch(new System.Drawing.Point(maxLoc.X, maxLoc.Y), new System.Drawing.Size(pic.Cols, pic.Rows), threshold)
                        {
                            Score = maxValue
                        };
                    }
                    else
                    {
                        retc.Score = maxValue;
                        retc.Rect = new System.Drawing.Rectangle(new System.Drawing.Point(maxLoc.X, maxLoc.Y), new System.Drawing.Size(pic.Cols, pic.Rows));
                    }
                    return true;
                }
                else
                {
                    return false;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>
        /// 图片包含匹配、大小图匹配带定位
        /// </summary>
        /// <param name="bigImagePath"></param>
        /// <param name="smallImagePath"></param>
        /// <param name="retc"></param>
        /// <param name="bitmap">生成的标记图</param>
        /// <param name="imgWidth"></param>
        /// <param name="threshold"></param>
        /// <returns>是否匹配</returns>
        public static bool IsImageContained(string bigImagePath, string smallImagePath, ref Model.RectangleMatch? retc, out System.Drawing.Bitmap? bitmap, double imgWidth = 0, double threshold = 0.65f)
        {
            try
            {

                using Mat source = new Mat(bigImagePath);
                if (imgWidth != 0)
                {
                    //如果源图宽度不等于对比图的宽度则按照指定宽度等比例缩放（提高识别率）
                    if (source.Width != imgWidth)
                    {
                        Cv2.Resize(source, source, new OpenCvSharp.Size(imgWidth, imgWidth * source.Height / (double)source.Width));
                    }
                }

                using Mat pic = new Mat(smallImagePath, ImreadModes.AnyColor);
                using Mat result = new Mat();
                //模板匹配
                Cv2.MatchTemplate(source, pic, result, TemplateMatchModes.CCoeffNormed);
                Point minLoc, maxLoc;
                double minValue, maxValue;
                Cv2.MinMaxLoc(result, out minValue, out maxValue, out minLoc, out maxLoc);
                Console.WriteLine(maxValue);
                if (maxValue >= threshold)
                {
                    using Mat mask = source.Clone();
                    //画框显示
                    Cv2.Rectangle(mask, maxLoc, new Point(maxLoc.X + pic.Cols, maxLoc.Y + pic.Rows), Scalar.Yellow, 2);
                    if (retc == null)
                    {
                        retc = new Model.RectangleMatch(new System.Drawing.Point(maxLoc.X, maxLoc.Y), new System.Drawing.Size(pic.Cols, pic.Rows), threshold)
                        {
                            Score = maxValue
                        };
                    }
                    else
                    {
                        retc.Score = maxValue;
                        retc.Rect = new System.Drawing.Rectangle(new System.Drawing.Point(maxLoc.X, maxLoc.Y), new System.Drawing.Size(pic.Cols, pic.Rows));
                    }
                    bitmap = mask.ToBitmap();
                    return true;
                }
                else
                {
                    if (retc == null)
                    {
                        retc = new Model.RectangleMatch(System.Drawing.Point.Empty, System.Drawing.Size.Empty, threshold)
                        {
                            Score = maxValue
                        };
                    }
                    bitmap = null;
                    return false;
                }
            }
            catch (Exception)
            {
                bitmap = null;
                return false;
            }
        }

        /// <summary>
        /// 相似度比较
        /// </summary>
        /// <param name="imgFile1"></param>
        /// <param name="imgFile2"></param>
        /// <returns></returns>
        public static Scalar CompareImagesSSIM(string imgFile1, string imgFile2)
        {
            using (var image1 = Cv2.ImRead(imgFile1))
            using (var image2Tmp = Cv2.ImRead(imgFile2))
            {
                // 将两个图片处理成同样大小，否则会有错误： 
                var image2 = new Mat();
                Cv2.Resize(image2Tmp, image2, new OpenCvSharp.Size(image1.Size().Width, image1.Size().Height));
                double C1 = 6.5025, C2 = 58.5225;
                var validImage1 = new Mat();
                var validImage2 = new Mat();
                Parallel.Invoke(
                    () => { image1.ConvertTo(validImage1, MatType.CV_32F); },
                   () => { image2.ConvertTo(validImage2, MatType.CV_32F); }

                );
                //image1.ConvertTo(validImage1, MatType.CV_32F); //数据类型转换为 float,防止后续计算出现错误
                //image2.ConvertTo(validImage2, MatType.CV_32F);

                Mat image1_1 = validImage1.Mul(validImage1); //图像乘积
                Mat image2_2 = validImage2.Mul(validImage2);
                Mat image1_2 = validImage1.Mul(validImage2);


                Mat gausBlur1 = new Mat(), gausBlur2 = new Mat(), gausBlur12 = new Mat();
                Cv2.GaussianBlur(validImage1, gausBlur1, new OpenCvSharp.Size(11, 11), 1.5); //高斯卷积核计算图像均值
                Cv2.GaussianBlur(validImage2, gausBlur2, new OpenCvSharp.Size(11, 11), 1.5);
                Cv2.GaussianBlur(image1_2, gausBlur12, new OpenCvSharp.Size(11, 11), 1.5);

                Parallel.Invoke(
                   () => { }
                   );

                Mat imageAvgProduct = gausBlur1.Mul(gausBlur2); //均值乘积
                Mat u1Squre = gausBlur1.Mul(gausBlur1); //各自均值的平方
                Mat u2Squre = gausBlur2.Mul(gausBlur2);

                Mat imageConvariance = new Mat(), imageVariance1 = new Mat(), imageVariance2 = new Mat();
                Mat squreAvg1 = new Mat(), squreAvg2 = new Mat();
                Cv2.GaussianBlur(image1_1, squreAvg1, new OpenCvSharp.Size(11, 11), 1.5); //图像平方的均值
                Cv2.GaussianBlur(image2_2, squreAvg2, new OpenCvSharp.Size(11, 11), 1.5);

                imageConvariance = gausBlur12 - gausBlur1.Mul(gausBlur2);// 计算协方差
                imageVariance1 = squreAvg1 - gausBlur1.Mul(gausBlur1); //计算方差
                imageVariance2 = squreAvg2 - gausBlur2.Mul(gausBlur2);

                var member = ((2 * gausBlur1.Mul(gausBlur2) + C1).Mul(2 * imageConvariance + C2));
                var denominator = ((u1Squre + u2Squre + C1).Mul(imageVariance1 + imageVariance2 + C2));
                Mat ssim = new Mat();
                Cv2.Divide(member, denominator, ssim);
                var sclar = Cv2.Mean(ssim);

                validImage1.Dispose();
                validImage2.Dispose();
                image1_1.Dispose();
                image2_2.Dispose();
                image1_2.Dispose();
                gausBlur1.Dispose();
                gausBlur2.Dispose();
                gausBlur12.Dispose();
                imageAvgProduct.Dispose();
                u1Squre.Dispose();
                u2Squre.Dispose();
                imageConvariance.Dispose();
                imageVariance1.Dispose();
                imageVariance2.Dispose();
                squreAvg1.Dispose();
                squreAvg2.Dispose();
                ssim.Dispose();

                GC.Collect();
                return sclar;  // 变化率，即差异
            }
        }
    }
}
